﻿@using SixLabors.ImageSharp.Processing
@using SixLabors.ImageSharp
@using SixLabors.ImageSharp.Formats.Png
@using CleanArchitecture.Blazor.Application.Features.Documents.Commands.AddEdit
@using CleanArchitecture.Blazor.Application.Features.Documents.Commands.Upload
@using CleanArchitecture.Blazor.Domain.Common.Enums
@using System.Buffers
@using CleanArchitecture.Blazor.Server.UI.Services.JsInterop

@inject IUploadService UploadService
@inject IStringLocalizer<Documents> L

<MudDialog>
    <DialogContent>
        @if (_uploadedFiles.Any())
        {
            <div class="d-flex flex-column flex-grow-1">
                @foreach (var item in _uploadedFiles)
                {
                    <MudGrid Spacing="2">
                        <MudItem xs="4" Class="align-self-center">
                            <MudText Typo="Typo.body2" Inline="false" Style="text-overflow:ellipsis;overflow: hidden;">@item.FileName</MudText>
                        </MudItem>
                        <MudItem xs="5">
                            <MudProgressLinear Color="Color.Success" Value="@item.UploadedBytes" Max="@item.Size" Class="my-7"/>
                        </MudItem>
                        <MudItem xs="3" Class="align-self-center justify-end">
                            <MudText Typo="Typo.body2" Style="float: right;">(@FormatBytes(item.UploadedBytes) / @FormatBytes(item.Size))</MudText>
                        </MudItem>
                    </MudGrid>
                }
            </div>
        }
        else
        {
            <MudAlert Severity="Severity.Info"> @L["Please click [Choose Files] button to upload your photo."]</MudAlert>
        }
    </DialogContent>
    <DialogActions>

        <MudButton Variant="Variant.Filled" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Clear" Disabled="@(!_uploadedFiles.Any())" OnClick="Clear">@ConstantString.Clear</MudButton>
        <MudFileUpload @ref="@_fileUpload" T="IReadOnlyList<IBrowserFile>" Accept=".jpg, .jpeg, .png, .webp" FilesChanged="LoadFiles">
            <ActivatorContent>
                <MudLoadingButton Loading="@_uploading" Variant="Variant.Filled"  
                StartIcon="@Icons.Material.Filled.AttachFile"
                >
                    @L["Choose files"]
                </MudLoadingButton>
            </ActivatorContent>
        </MudFileUpload>

        <MudLoadingButton Loading="@_processing" Disabled="@(!_uploadedFiles.Any())" Variant="Variant.Filled" OnClick="Submit">@ConstantString.Submit</MudLoadingButton>

    </DialogActions>
</MudDialog>

@code{
    [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!;

    [EditorRequired] [Parameter] public AddEditDocumentCommand Model { get; set; } = default!;
    private MudFileUpload<IReadOnlyList<IBrowserFile>> _fileUpload=default!;
    private bool _processing;
    private bool _uploading;
    private List<FileUploadProgress> _uploadedFiles = new();

    private async void LoadFiles(IReadOnlyList<IBrowserFile> files)
    {
        if (files is null || !files.Any()) return;
        try
        {

            _uploading = true;
            var startIndex = _uploadedFiles.Count;
            // Add all files to the UI
            _uploadedFiles.AddRange(files.Select(file => new FileUploadProgress(file.Name, file.Size, file)).ToList());

            // We don't want to refresh the UI too frequently,
            // So, we use a timer to update the UI every few hundred milliseconds
            await using var timer = new Timer(_ => InvokeAsync(() => StateHasChanged()));
            timer.Change(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(500));

            // Upload files
            var buffer = ArrayPool<byte>.Shared.Rent(4096);
            try
            {
                foreach (var file in files)
                {
                    using (var stream = file.OpenReadStream(GlobalVariable.MaxAllowedSize))
                    {
                        while (true)
                        {
                            var read = await stream.ReadAsync(buffer);
                            if (read == 0)
                            {
                                break; // Exit loop if no more data to read
                            }

                            _uploadedFiles[startIndex].UploadedBytes += read;

                            // TODO Do something with the file chunk, such as save it
                            // to a database or a local file system
                            var readData = buffer.AsMemory().Slice(0, read);
                        }
                    }

                    startIndex++;
                }
            }
            finally
            {
                ArrayPool<byte>.Shared.Return(buffer);

                // AddOrUpdate the UI with the final progress
                StateHasChanged();
            }
        }
        finally
        {
            _uploading = false;
            StateHasChanged();
        }
    }

    // Use the Meziantou.Framework.ByteSize NuGet package.
    // You could also use Humanizer
    private string FormatBytes(long byteCount)
    {
        string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
        if (byteCount == 0)
            return "0" + suf[0];
        var bytes = Math.Abs(byteCount);
        var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
        var num = Math.Round(bytes / Math.Pow(1024, place), 1);
        return Math.Sign(byteCount) * num + suf[place];
    }

    private async Task Submit()
    {
        try
        {
            _processing = true;
            if (_uploadedFiles.Any())
            {
                var list = new List<UploadRequest>();
                foreach (var uploaded in _uploadedFiles)
                {
                    try
                    {
                        var filestream = uploaded.File.OpenReadStream(GlobalVariable.MaxAllowedSize);
                        var imgStream = new MemoryStream();
                        await filestream.CopyToAsync(imgStream);
                        imgStream.Position = 0;
                        using (var outStream = new MemoryStream())
                        {
                            using (var image = Image.Load(imgStream))
                            {
                                var scale = 0m;
                                if (image.Width > 1600)
                                {
                                    scale = 0.3m;
                                }
                                else if (image.Width <= 1600 && image.Width > 800)
                                {
                                    scale = 0.5m;
                                }
                                else
                                {
                                    scale = 0.9m;
                                }

                                image.Mutate(
                                    i => i.AutoOrient().Resize(Convert.ToInt32(image.Width * scale), 0));
                                image.Save(outStream, PngFormat.Instance);
                            }

                            var request = new UploadRequest(uploaded.FileName, UploadType.Document, outStream.ToArray());
                            list.Add(request);
                        }
                    }
                    catch (Exception e)
                    {
                        Snackbar.Add($"{e.Message}", Severity.Error);
                    }
                }
                if (list.Any()){
                    var uploadCommand = new UploadDocumentCommand(list);
                    await Mediator.Send(uploadCommand);
                    await Clear();
                    MudDialog.Close(DialogResult.Ok(true));
                }
                
            }
        }
        finally
        {
            _processing = false;
        }
    }

    private void Cancel()
    {
        MudDialog.Cancel();
    }

    private  Task Clear()
    {
        _uploadedFiles.Clear();
        return _fileUpload?.ClearAsync()??Task.CompletedTask;
    }

    private record FileUploadProgress(string FileName, long Size, IBrowserFile File)
    {
        public long UploadedBytes { get; set; }
        public double UploadedPercentage => UploadedBytes / (double)Size * 100d;
    }

}